package json;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.io.IOUtils;
import org.bson.Document;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import utils.MongoUtils;

public class JSONUtilsMemoryAndCpuUsage {

	public static final String MEMORY_AND_CPU_USAGE_COLLECTION = "memory_and_cpu_usage";
	public static final String JSON_UTILS_DATABASE = "json_utils";

	public static void main(String[] args) {
		
		String jsonUtil = args.length>0?args[0]:"jackson";
		int count = args.length>1?Integer.parseInt(args[1]):1000;
		int threadCount = 1+args.length>2?Integer.parseInt(args[2]):5;
		/*
		* jackson,jacksonSingleton,fastjson2,gson,gsonSingleton,jodd,jsonInJava,dsl,dslSingleton,avaje,avajeSingleton
		 * 
		 */
		System.out.println("testing "+jsonUtil);
		var threadPool = Executors.newFixedThreadPool(threadCount);
		var left = new AtomicInteger(count);
		Collection<Map<String, Object>> messages = new ConcurrentLinkedQueue<>();
		threadPool.execute(()->{
			while(left.get()>=0) {
				int leftCount = left.get();
				int finished = count-leftCount;
				System.out.println("finished "+finished);
				var memAndCpuUsage = getMemoryAndCpuUsage();
				Map<String, Object> message = new HashMap<>(memAndCpuUsage);
				message.put("finished", finished);
				message.put("time", System.currentTimeMillis());
				messages.add(message);
				if(leftCount==0) {
					break;
				}
				try {
					Thread.sleep(50L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			try {
				System.out.println("messages: "+new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(messages));
				saveResult(jsonUtil, messages);
			} catch (JsonProcessingException e) {
				e.printStackTrace();
			}
		});
		for(int i=0;i<count;i++) {
			threadPool.execute(()->{
				try {
					switch (jsonUtil) {
					case "jackson":
						JSONUtilsBenchmark.jacksonTest();
						break;
					case "jacksonSingleton":
						JSONUtilsBenchmark.jacksonSingletonTest();
						break;
					case "fastjson2":
						JSONUtilsBenchmark.fastjson2Test();
						break;
					case "gson":
						JSONUtilsBenchmark.gsonTest();
						break;
					case "gsonSingleton":
						JSONUtilsBenchmark.gsonSingletonTest();
						break;
					case "jodd":
						JSONUtilsBenchmark.joddTest();
						break;
					case "jsonInJava":
						JSONUtilsBenchmark.jsonInJavaTest();
						break;
					case "dsl":
						JSONUtilsBenchmark.dslTest();
						break;
					case "dslSingleton":
						JSONUtilsBenchmark.dslSingletonTest();
						break;
					case "avaje":
						JSONUtilsBenchmark.avajeTest();
						break;
					case "avajeSingleton":
						JSONUtilsBenchmark.avajeSingletonTest();
						break;
					default:
						break;
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				left.getAndDecrement();
			});
		}
		threadPool.shutdown();
	}
	
	private static void saveResult(String jsonUtil, Collection<Map<String, Object>> messages) {
		MongoUtils.getDatabase(JSON_UTILS_DATABASE).getCollection(MEMORY_AND_CPU_USAGE_COLLECTION)
			.insertOne(new Document("jsonUtil", jsonUtil)
					.append("messages", messages)
					.append("updatedTime", new Date()));
	}
	
	private static Map<String, Object> getMemoryAndCpuUsage() {
		String name = ManagementFactory.getRuntimeMXBean().getName();
		String pid = name.split("@")[0];
		Map<String, Object> result = new HashMap<>();
		try {
			result.put("freeMem", Runtime.getRuntime().freeMemory());
			result.put("totalMem", Runtime.getRuntime().totalMemory() );
			String cpuPercent = IOUtils.readLines(
					Runtime.getRuntime().exec("/usr/bin/ps -p " + pid + " -o THREAD").getInputStream(),
					StandardCharsets.UTF_8).get(1).split(" +")[1];
			result.put("cpuPercent", Double.parseDouble(cpuPercent));
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		return result;
	}
}
